/**
 * 
 */
package com.sidom.movieIndexer.service.imdb;

import static org.apache.commons.lang.Validate.notNull;

import java.io.IOException;
import java.util.List;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.ResponseHandler;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.BasicResponseHandler;
import org.apache.http.impl.client.DefaultHttpClient;
import org.htmlcleaner.CleanerProperties;
import org.htmlcleaner.HtmlCleaner;
import org.htmlcleaner.JDomSerializer;
import org.htmlcleaner.TagNode;
import org.jdom.Document;
import org.jdom.Element;
import org.springframework.stereotype.Repository;

import com.sidom.movieIndexer.domain.Movie;
import com.sidom.movieIndexer.domain.google.Match;
import com.sidom.movieIndexer.domain.imdb.ImdbResult;
import com.sidom.movieIndexer.domain.imdb.ImdbResult.Builder;
import com.sidom.movieIndexer.domain.imdb.ImdbResult.Label;

/**
 * {@inheritDoc}.
 * @author Siebe
 */
@Repository
public class ImdbServiceImpl implements ImdbService{
	/**
	 * Logger for {@link ImdbServiceImpl}.
	 */
	private Log log = LogFactory.getLog(ImdbServiceImpl.class);
	
	/**
	 * {@inheritDoc}.
	 */
	@Override
	public List<Movie> addImdbInformation(List<Movie> movies) {
		log.info("Add imdb information to movies: " + movies);
		notNull(movies, "Movies should not be null");
		for (Movie movie : movies) {
			notNull(movie.getMovieFile(), "The movieFile should not be null");
			notNull(movie.getGoogleSearchResult(), "The Google search result should not be null");
			if (movie.getGoogleSearchResult().getMatch()!=Match.NOT_FOUND) {
				log.trace("Parsing html for movie: " + movie.getMovieFile().getCleanedFileName());
				movie.setImdbResult(parseImdbResult(movie.getGoogleSearchResult().getImdbUrl()));
			}
		}
		log.trace("IMDB information added");
		return movies;
	}
	
	/**
	 * Parse the {@link ImdbResult} from an imdbUrl.
	 * @return The {@link ImdbResult}.
	 */
	private ImdbResult parseImdbResult(String imdbUrl){
		String html 			= getHtml(imdbUrl);
		Document document 		= getCleanedDocument(html);
		Element contentDiv		= ImdbScreenScraper.getContentDiv(document);
		List<Element> infoDivs 	= ImdbScreenScraper.getInfoDivs(contentDiv);
		Builder builder 		= new ImdbResult.Builder();
		addInfoDivInformationToBuilder(infoDivs, builder);
		builder.title(ImdbScreenScraper.getTitle(document));
		builder.year(ImdbScreenScraper.getYear(document));
		builder.coverUrl(ImdbScreenScraper.getCoverUrl(document));
		builder.userRating(ImdbScreenScraper.getUserRating(document));
		return new ImdbResult(builder);
	}
	
	/**
	 * Get the html code from an imdbUrl.
	 * This is done by using HttpClient library from apache.
	 * @param imdbUrl The imdbUrl.
	 * @return the html string.
	 */
	private String getHtml(String imdbUrl){
		log.trace("Getting html response for: " + imdbUrl);
        HttpGet httpget = new HttpGet(imdbUrl); 
        ResponseHandler<String> responseHandler = new BasicResponseHandler();
        String result = "";
        HttpClient httpClient = new DefaultHttpClient(); 
        try {
			result = httpClient.execute(httpget, responseHandler);
		} catch (ClientProtocolException e) {
			log.error("Problem getting html for movie with imdbUrl: " + imdbUrl, e);
		} catch (IOException e) {
			log.error("Problem getting html for movie with imdbUrl: " + imdbUrl, e);
		}finally{
			httpClient.getConnectionManager().shutdown();  
		}
		log.trace("html result for: " + imdbUrl + " is: " + result);
		return result;
	}
	
	/**
	 * Get a cleaned html document from the html string.
	 * Use HtmlCleaner library to to this.
	 * @param html The html string.
	 * @return The cleaned html represented as a {@link Document}.
	 */
	private Document getCleanedDocument(String html){
		HtmlCleaner cleaner = new HtmlCleaner();
		CleanerProperties props = cleaner.getProperties();
		TagNode node = null;
		try {
			node = cleaner.clean(html);
		} catch (IOException e) {
			log.error("Failed to clean html", e);
		}
		return new JDomSerializer(props, true).createJDom(node);
	}
	
	/**
	 * Add info div information to the {@link Builder}
	 * @param infoDivs A {@link List} of infoDiv {@link Element}s.
	 * @param builder The {@link ImdbResult} {@link Builder}.
	 */
	private void addInfoDivInformationToBuilder(List<Element> infoDivs, Builder builder){
		builder
			.directors(ImdbScreenScraper.getListContentFromInfoDivs(infoDivs, Label.DIRECTOR))
			.writers(ImdbScreenScraper.getListContentFromInfoDivs(infoDivs, Label.WRITER))
			.releaseDate(ImdbScreenScraper.getTextContentFromInfoDivs(infoDivs, Label.RELEASE_DATE))
			.genres(ImdbScreenScraper.getListContentFromInfoDivs(infoDivs, Label.GENRE))
			.tagLine(ImdbScreenScraper.getTextContentFromInfoDivs(infoDivs, Label.TAG_LINE))
			.plot(ImdbScreenScraper.getTextContentFromInfoDivs(infoDivs, Label.PLOT))
			.plotKeyWords(ImdbScreenScraper.getPlotKeyWordsListContentFromInfoDivs(infoDivs, Label.PLOT_KEYWORD))
			.userReview(ImdbScreenScraper.getTextContentFromInfoDivs(infoDivs, Label.USER_REVIEW))
			.castMembers(ImdbScreenScraper.getCastListContentFromInfoDivs(infoDivs, Label.CAST));
	}
}
